Skip to content

Conversation

2qp
Copy link

@2qp 2qp commented Sep 30, 2025

PR: Improve Registry Path Handling in Monorepo Environments

πŸ› οΈ Changes Summary

  • replace inline original file creation with createRegistryFile utility
  • added Type alias RegistryItemFile of registryItemFileSchema

Reason

  • upcoming case handling change under if (!stats.isFile()) { of recursivelyResolveFileImports will reuse createRegistryFile.

PR roadmap introduces several refactors and new utility functions to improve how the shadcn registry:build CLI handles failed aliased import paths, especially in monorepo setups using path aliases (e.g. @/components/...).

πŸ› Problem

In monorepos, adding a registry component like:

{
  "files": [
    {
      "path": "@/components/rhf/rhf-text-field.tsx",
      "type": "registry:component"
    }
  ]
}

...and running:

pnpm shadcn registry:build

Would fail with:

ENOENT: no such file or directory, stat '/absolute/path/to/ui/apps/registry/@/components/ui/rhf/rhf-text-field.tsx'

fs.stat() is called before the alias is resolved by tsconfig-paths, causing the CLI to crash ungracefully.


πŸ’‘ Why This Happens

In a monorepo, files from multiple workspaces may be referenced using aliases (@, @ui, etc.). If these aliases aren't fully resolved to disk paths, the CLI crashes when trying to access them directly via fs.stat.

The current logic fails here:

// packages/shadcn/src/registry/utils.ts
const stat = await fs.stat(resolvedFilePath);
if (!stat.isFile()) {
  // Optionally log or handle this case
  return { dependencies: [], files: [] };
}

When fs.stat() is called on an invalid or unresolved path, it throws an ENOENT error, causing the CLI to crash ungracefully.


🧭 Roadmap

part of a multi step effort to enable the registry CLI to handle path aliases gracefully in monorepo environments.

  • Refactor: – move determineFileType() to its own file (utils/registry/determine-file-type.ts) πŸš€ (awaiting merge refactor(registry/utils): move determineFileType to determine-file-type.tsΒ #8299)
  • Refactor: – add createRegistryFile() util for creating registry file shape βœ… (this PR)
  • Refactor: – add getStatsOrNonFile() to gracefully suppress ENOENT errors
  • Refactor: – add retryAlternativePath() to retry common alias misresolves
  • Feature: – inject fallback logic into !stat.isFile() and handle returned suppressed files in registry build command's resolveRegistryItems
  • Feature – add warning/logging system for invalid file paths (follow-up)

Each change is isolated for clean diffs.


🧱 Directory Structure (for Context)

my monorepo structure to understand the issue:

./
β”œβ”€β”€ apps/registry/ # registry
β”‚   β”œβ”€β”€ components.json, package.json, registry.json
β”‚   β”œβ”€β”€ public/r/ β†’ registry.json, rhf-text-field.json, theme.json
β”‚   └── src/
β”‚       β”œβ”€β”€ app/
β”‚       β”‚   β”œβ”€β”€ (registry)/
β”‚       β”‚   └── demo/[name]/ β†’ components/rhf-text-field.tsx, ui/ # demo
β”‚       β”œβ”€β”€ components/ β†’ registry/, rhf-text-field.tsx # example
β”‚       β”œβ”€β”€ content/, hooks/, layouts/
β”‚       └── lib/ β†’ highlight-code.ts, products.ts, registry.ts, utils.ts
β”‚       β”œβ”€β”€ tsconfig.json, tsconfig.tsbuildinfo
β”œβ”€β”€ package.json
β”œβ”€β”€ packages/
β”‚   β”œβ”€β”€ eslint-config/, hooks/, typescript-config/
β”‚   β”œβ”€β”€ shadcn-ui/ # shadcn-ui repo
β”‚   β”‚   β”œβ”€β”€ components.json, package.json, tsconfig.json
β”‚   β”‚   └── src/
β”‚   β”‚       β”œβ”€β”€ components/ui/ β†’ button.tsx, form.tsx, input.tsx, label.tsx
β”‚   β”‚       β”œβ”€β”€ hooks/, lib/utils.ts, styles/globals.css
β”‚   β”œβ”€β”€ shadcn-ui-extended/ # extended component repo `rhf-text-field.tsx`
β”‚   β”‚   β”œβ”€β”€ package.json, tsconfig.json
β”‚   β”‚   └── src/
β”‚   β”‚       β”œβ”€β”€ components/ β†’ rhf/rhf-text-field.tsx, ui/
β”‚   β”‚       └── data/, helpers/, hooks/, lib/, utils/
β”‚   └── ui/turbo/generators/
└── pnpm-lock.yaml, pnpm-workspace.yaml, turbo.json

tsconfig.json

    "paths": {
      // ...
      "@/components/ui/*": [
        "../../packages/shadcn-ui/src/components/ui/*", // primary: shadcn-ui package
        "src/components/ui/*" // fallback: local overrides
      ],

      "@/components/rhf/*": [
        "../../packages/shadcn-ui-extended/src/components/rhf/*", // shadcn-ui-extended
        "src/components/rhf/*" // local overrides
      ],
    }

πŸ“š Notes

  • tsconfig-paths is used, but current fs.stat() happens before alias resolution in some cases.
  • roadmap feat provides resilience without adding external dependencies or needing a separate alias parser (yet).

πŸ”— Follow-Ups

  • Log warnings for files that fail even after retry fallback

Copy link

vercel bot commented Sep 30, 2025

@2qp is attempting to deploy a commit to the shadcn-pro Team on Vercel.

A member of the Team first needs to authorize it.

@2qp 2qp changed the title Refactor/move add original file to utils refactor(registry/utils): replace inline original file creation with createRegistryFile utility Sep 30, 2025
@2qp 2qp changed the title refactor(registry/utils): replace inline original file creation with createRegistryFile utility refactor(registry/utils): replace inline original file creation with createRegistryFile utility Sep 30, 2025
2qp added 2 commits October 5, 2025 02:29
…to `utils/registry/determine-file-type.ts` avoid upcoming circular deps

- `createFileObject` (to be imported into `utils.ts`) will use `determineFileType`, which currently lives in `utils.ts`. - moving it out prevents circular imports between `utils.ts` and `createFileObject`.
…`createRegistryFile` utility

- improve modularity and upcoming reusability
- added Type alias `RegistryItemFile` of `registryItemFileSchema`
@2qp 2qp force-pushed the refactor/move-add-original-file-to-utils branch from 5aef4a9 to 97c8015 Compare October 4, 2025 21:02
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

1 participant